package vanjalee.bs.com.control;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.supercsv.cellprocessor.ParseBool;
import org.supercsv.cellprocessor.ParseInt;
import org.supercsv.cellprocessor.constraint.NotNull;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;

import vanjalee.bs.com.FileSearch;
import vanjalee.bs.com.control.csv.ParseItemType;
import vanjalee.bs.com.control.model.Item;
import vanjalee.bs.com.control.model.Part;
import vanjalee.bs.com.control.model.PartType;
import vanjalee.bs.com.control.model.Weapon;
import vanjalee.bs.com.control.model.WeaponType;

public class ImportExportController {

	private final static String WEAPON_CLOSE_COMBAT = "weapon_close_combat.csv";
	private final static String WEAPON_LONG_RANGE = "weapon_long_range.csv";
	private final static String WEAPON_MAIN_CANNON = "weapon_main_cannon.csv";
	private final static String WEAPON_SUBGUN = "weapon_subgun.csv";

	private final static String PART_ENGINE = "part_engine.csv";
	private final static String PART_FIGUREHEAD = "part_figurehead.csv";
	private final static String PART_HULL = "part_hull.csv";
	private final static String PART_STABILIZER = "part_stabilizer.csv";
	private final static String PART_SAIL = "part_sail.csv";

	private ICsvBeanWriter beanWriter;
	private ICsvBeanReader beanReader;

	public ImportExportController() {
		setProxy();
	}

	private void setProxy() {
		String proxySet = StaticValuesController.getValueFor("proxySet");
		if ("true".equals(proxySet)) {
			System.getProperties().put("proxySet", proxySet);
			System.getProperties().put("proxyHost",
					StaticValuesController.getValueFor("proxy.host"));
			System.getProperties().put("proxyPort",
					StaticValuesController.getValueFor("proxy.port"));
		}
	}

	public void updateCSVFiles() {
		updateWeaponFilesWithWiki(StaticValuesController.weaponCloseCombatUrl,
				WeaponType.CLOSE_COMBAT);
		updateWeaponFilesWithWiki(StaticValuesController.weaponSubgunUrl,
				WeaponType.SUBGUN);
		updateWeaponFilesWithWiki(StaticValuesController.weaponLongRangeUrl,
				WeaponType.LONG_RANGE);
		updateWeaponFilesWithWiki(StaticValuesController.weaponMainCannonUrl,
				WeaponType.MAIN_CANNON);
		updatePartFilesWithWiki(StaticValuesController.partEngineUrl,
				PartType.ENGINE);
		updatePartFilesWithWiki(StaticValuesController.partSailUrl,
				PartType.SAIL);
		updatePartFilesWithWiki(StaticValuesController.partStabilizerUrl,
				PartType.STABILIZER);
		updatePartFilesWithWiki(StaticValuesController.partHullUrl,
				PartType.HULL);
		updatePartFilesWithWiki(StaticValuesController.partFigureheadUrl,
				PartType.FIGUREHEAD);
	}

	private void updateWeaponFilesWithWiki(String pageUrl, WeaponType weaponType) {
		HashMap<String, Weapon> weapons = new HashMap<String, Weapon>();
		try {
			NumberFormat nf = NumberFormat.getNumberInstance(Locale.ENGLISH);
			int page = 1;
			Document doc = null;
			Element tableBody;
			Elements tableRows;
			Element row;
			String concretePage;
			while (page < 30) {
				concretePage = pageUrl + page++;
				doc = Jsoup.connect(concretePage).get();
				tableBody = doc.getElementsByTag("tbody").first();
				tableRows = tableBody.getElementsByTag("tr");
				for (int i = 1; i < tableRows.size(); i++) {
					row = tableRows.get(i);
					Elements values = row.getElementsByTag("td");
					String name = values.get(1).text();
					Elements nameAndPic = values.get(1).getElementsByTag("div");
					name = nameAndPic
							.get(1)
							.text()
							.substring(0,
									nameAndPic.get(1).text().lastIndexOf(" "));
					if (weapons.containsKey(name)) {
						continue;
					}

					Weapon weapon = new Weapon();
					weapon.setWeaponType(weaponType);
					weapon.setName(name);
					weapon.setLevel(nf.parse(values.get(2).text()).intValue());
					weapon.setRange(nf.parse(values.get(3).text()).intValue());
					String damage = values.get(4).text();
					String[] splitDamage = damage.split("-");
					weapon.setMinDmg(nf.parse(splitDamage[0]).intValue());
					weapon.setMaxDmg(nf.parse(splitDamage[1]).intValue());
					weapon.setAvgDmg(nf.parse(values.get(5).text()).intValue());
					weapon.setWeight(nf.parse(values.get(6).text()).intValue());
					weapon.setDmgWeight(nf.parse(values.get(7).text())
							.intValue());
					weapon.setSlowdown(nf.parse(values.get(8).text())
							.intValue());
					weapon.setWhereToGet(values.get(9).text());
					weapon.setEquipLimit(nf.parse(values.get(10).text())
							.intValue());
					Item.setItemType(weapon, values.get(11).text());
					if (weapon.getFurtherInfo() == null
							|| weapon.getFurtherInfo().length() <= 0) {
						weapon.setFurtherInfo("-");
					}
					if (weapon.getFurtherInfo().contains("Ocho Shop ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Ocho Shop", "Ocho Shop,"));
					}
					if (weapon.getFurtherInfo().contains("Upgradable ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Upgradable", "Upgradable,"));
					}
					if (weapon.getFurtherInfo().contains("Assembled ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Assembled", "Assembled,"));
					}
					if (weapon.getFurtherInfo().contains("Crafted ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Crafted", "Crafted,"));
					}
					if (weapon.getFurtherInfo().contains("Maelstrom Event ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Maelstrom Event", "Maelstrom Event,"));
					}
					if (weapon.getFurtherInfo().contains("Halloween Event ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Halloween Event", "Halloween Event,"));
					}
					if (weapon.getFurtherInfo().contains("Ember Item ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Ember Item", "Ember Item,"));
					}
					if (weapon.getFurtherInfo().contains("Casino Prize ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Casino Prize", "Casino Prize,"));
					}
					if (weapon.getFurtherInfo().contains("Great Hunt item.")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								"Great Hunt item.", "Great Hunt Event,"));
					}
					if (weapon.getFurtherInfo().contains(". ")) {
						weapon.setFurtherInfo(weapon.getFurtherInfo().replace(
								". ", ", "));
					}
					weapons.put(name, weapon);
				}
				List<Weapon> fetchedWeapons = new ArrayList<Weapon>(
						weapons.values());
				Collections.sort(fetchedWeapons, Weapon
						.getComparator(Weapon.SortParameter.LEVEL_ASCENDING));
				exportWeapons(fetchedWeapons, weaponType);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}

	private void updatePartFilesWithWiki(String pageUrl, PartType partType) {
		HashMap<String, Part> parts = new HashMap<String, Part>();
		try {
			NumberFormat nf = NumberFormat.getNumberInstance(Locale.ENGLISH);
			int page = 1;
			Document doc = null;
			Element tableBody;
			Elements tableRows;
			Element row;
			String concretePage;
			while (page < 30) {
				concretePage = pageUrl + page++;
				doc = Jsoup.connect(concretePage).get();
				tableBody = doc.getElementsByTag("tbody").first();
				tableRows = tableBody.getElementsByTag("tr");
				for (int i = 1; i < tableRows.size(); i++) {
					row = tableRows.get(i);
					Elements values = row.getElementsByTag("td");
					String name = values.get(1).text();
					Elements nameAndPic = values.get(1).getElementsByTag("div");
					name = nameAndPic
							.get(1)
							.text()
							.substring(0,
									nameAndPic.get(1).text().lastIndexOf(" "));
					if (parts.containsKey(name)) {
						page = 30;
						break;
					}
					Part part = new Part();
					part.setPartType(partType);
					part.setName(name);
					part.setLevel(nf.parse(values.get(2).text()).intValue());
					part.setSpeed(nf.parse(values.get(3).text()).intValue());
					part.setHp(nf.parse(values.get(4).text()).intValue());
					part.setCapacity(nf.parse(values.get(5).text()).intValue());
					part.setArmor(nf.parse(values.get(6).text()).intValue());
					part.setWeight(nf.parse(values.get(7).text()).intValue());
					part.setWhereToGet(values.get(8).text());
					Item.setItemType(part, values.get(9).text());
					if (part.getFurtherInfo() == null
							|| part.getFurtherInfo().length() <= 0) {
						part.setFurtherInfo("-");
					}
					parts.put(name, part);
				}
				List<Part> fetchedParts = new ArrayList<Part>(parts.values());
				Collections.sort(fetchedParts,
						Part.getComparator(Part.SortParameter.LEVEL_ASCENDING));
				exportParts(fetchedParts, partType);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}

	public void exportWeapons(List<Weapon> weapons, WeaponType weaponType) {
		try {
			setAppropriateBeanWriter(weaponType, null);
			final String[] header = new String[] { "name", "level", "range",
					"minDmg", "maxDmg", "avgDmg", "weight", "dmgWeight",
					"slowdown", "equipLimit", "itemType", "whereToGet",
					"furtherInfo", "upgradable", "special" };
			beanWriter.writeHeader(header);
			for (final Weapon weapon : weapons) {
				beanWriter.write(weapon, header);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (beanWriter != null) {
				try {
					beanWriter.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public void exportParts(List<Part> parts, PartType partType) {
		try {
			setAppropriateBeanWriter(null, partType);
			final String[] header = new String[] { "name", "level", "speed",
					"hp", "capacity", "armor", "weight", "itemType",
					"whereToGet", "furtherInfo", "upgradable", "special" };
			beanWriter.writeHeader(header);
			for (final Part part : parts) {
				beanWriter.write(part, header);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (beanWriter != null) {
				try {
					beanWriter.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public List<Weapon> importWeapons(WeaponType weaponType) {
		try {
			setAppropriateBeanReader(weaponType, null);
			final String[] header = beanReader.getHeader(true);
			Weapon weapon;
			List<Weapon> imported = new ArrayList<Weapon>();
			while ((weapon = beanReader.read(Weapon.class, header,
					processorsWeapon)) != null) {
				imported.add(weapon);
			}
			return imported;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (beanReader != null) {
				try {
					beanReader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public List<Part> importParts(PartType partType) {
		try {
			setAppropriateBeanReader(null, partType);
			final String[] header = beanReader.getHeader(true);
			Part part;
			List<Part> imported = new ArrayList<Part>();
			while ((part = beanReader.read(Part.class, header, processorsPart)) != null) {
				imported.add(part);
			}
			return imported;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (beanReader != null) {
				try {
					beanReader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	private void setAppropriateBeanWriter(WeaponType weaponType,
			PartType partType) throws IOException {
		if (weaponType != null) {
			if (weaponType.equals(WeaponType.CLOSE_COMBAT)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(WEAPON_CLOSE_COMBAT)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (weaponType.equals(WeaponType.MAIN_CANNON)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(WEAPON_MAIN_CANNON)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (weaponType.equals(WeaponType.LONG_RANGE)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(WEAPON_LONG_RANGE)),
						CsvPreference.STANDARD_PREFERENCE);
			} else {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(WEAPON_SUBGUN)),
						CsvPreference.STANDARD_PREFERENCE);
			}
		} else {
			if (partType.equals(PartType.ENGINE)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(PART_ENGINE)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.HULL)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(PART_HULL)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.STABILIZER)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(PART_STABILIZER)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.SAIL)) {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(PART_SAIL)),
						CsvPreference.STANDARD_PREFERENCE);
			} else {
				beanWriter = new CsvBeanWriter(new FileWriter(
						FileSearch.findFile(PART_FIGUREHEAD)),
						CsvPreference.STANDARD_PREFERENCE);
			}
		}
	}

	private void setAppropriateBeanReader(WeaponType weaponType,
			PartType partType) throws FileNotFoundException {
		if (weaponType != null) {
			if (weaponType.equals(WeaponType.CLOSE_COMBAT)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(WEAPON_CLOSE_COMBAT)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (weaponType.equals(WeaponType.MAIN_CANNON)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(WEAPON_MAIN_CANNON)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (weaponType.equals(WeaponType.LONG_RANGE)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(WEAPON_LONG_RANGE)),
						CsvPreference.STANDARD_PREFERENCE);
			} else {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(WEAPON_SUBGUN)),
						CsvPreference.STANDARD_PREFERENCE);
			}
		} else {
			if (partType.equals(PartType.ENGINE)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(PART_ENGINE)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.HULL)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(PART_HULL)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.STABILIZER)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(PART_STABILIZER)),
						CsvPreference.STANDARD_PREFERENCE);
			} else if (partType.equals(PartType.SAIL)) {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(PART_SAIL)),
						CsvPreference.STANDARD_PREFERENCE);
			} else {
				beanReader = new CsvBeanReader(new FileReader(
						FileSearch.findFile(PART_FIGUREHEAD)),
						CsvPreference.STANDARD_PREFERENCE);
			}
		}
	}

	final CellProcessor[] processorsWeapon = new CellProcessor[] {
			new NotNull(), new ParseInt(), new ParseInt(), new ParseInt(),
			new ParseInt(), new ParseInt(), new ParseInt(), new ParseInt(),
			new ParseInt(), new ParseInt(), new ParseItemType(), new NotNull(),
			new NotNull(), new ParseBool(), new ParseBool() };

	final CellProcessor[] processorsPart = new CellProcessor[] { new NotNull(),
			new ParseInt(), new ParseInt(), new ParseInt(), new ParseInt(),
			new ParseInt(), new ParseInt(), new ParseItemType(), new NotNull(),
			new NotNull(), new ParseBool(), new ParseBool() };
}
